package com.xflib.framework.log;

import cn.hutool.core.date.SystemClock;
import com.xflib.framework.common.ApplicationContext;
import org.slf4j.Logger;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * 执行cmd的输出处理器
 */
public class StreamGobbler extends Thread {
    public final String ERROR="ERROR";
    public final String OUTPUT="OUTPUT";
    //public final String INFO="INFO";

    InputStream is;
    String type;
    long timerOut = Integer.MAX_VALUE;
    long create_time = -1;
    boolean deBug=false;

    public StreamGobbler(InputStream is, String type) {
        this.is = is;
        this.type = type;
    }

    @Override
    public synchronized void start() {
        this.create_time = SystemClock.now();
        super.start();
    }

    public void run() {
        try (InputStreamReader isr = new InputStreamReader(is);
             BufferedReader br = new BufferedReader(isr);){
            String line=null;
            while ( (line = br.readLine()) != null) {
                appendWithDebug(/*type + ">" + */line, this.deBug);
                if(SystemClock.now()>(create_time+timerOut)){
                    appendWithDebug(/*type + ">" + */"Exec timer out!", true);
                    break;
                }
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

    public StreamGobbler setTimerOut(long timerOut) {
        this.timerOut = timerOut;
        return this;
    }

    public StreamGobbler setCreate_time(long create_time) {
        this.create_time = create_time;
        return this;
    }

    public StreamGobbler setDeBug(boolean deBug) {
        this.deBug = deBug;
        return this;
    }

    public static void appendWithDebug(Logger logger, String content, boolean debug) {
        if (null != logger && logger.isInfoEnabled()) {
            logger.info(content);
        } else {
            if(debug) {
                Monitor.appendWithDebug(logger,content,debug);
            }
        }
    }
    public static void appendWithDebug(String content, boolean debug) {
        appendWithDebug(null, content, debug);
    }
    public static void appendWithDebug(Logger logger, String content) {
        appendWithDebug(logger,content,true);
    }

    private static class Monitor {

        private static Executor executor = Executors.newSingleThreadExecutor();
        private static ConcurrentLinkedQueue<String> cache = new ConcurrentLinkedQueue<>();
        private static ConcurrentMap<String, Process> processMap = new ConcurrentHashMap<>();
        private static boolean  running=false;

        static void appendWithDebug(Logger logger, String content, boolean debug) {
            if (null != logger && logger.isInfoEnabled()) {
                logger.info(content);
            } else {
                if(debug) {
                    cache.add(content);
                }
                if(!running) {
                    start();
                }
            }
        }

        private static void run(){
            running=true;
            try {
                while (!ApplicationContext.isTerminated()) {
                    boolean o = false;
                    if (!cache.isEmpty()) {
                        System.out.println(cache.poll());
                    } else if (!processMap.isEmpty()) {
                        List<String> expired = new ArrayList<>();
                        processMap.forEach((k, v) -> {
                            String[] kv = k.split(":");
                            if (SystemClock.now() - Long.valueOf(kv[0]) > Long.valueOf(kv[1])) {
                                expired.add(k);
                            }

                        });
                        if (expired.size() > 0) {
                            expired.forEach(k -> {
                                Process p = processMap.remove(k);
                                if (null != p) {
                                    p.destroy();
                                }
                                p = null;
                            });
                        } else {
                            o = true;
                        }
                    } else {
                        o = true;
                    }
                    if (o) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }
                }
            }finally {
                running = false;
            }
        }

        private synchronized static void start(){
            if(!running) {
                running=true;
                executor.execute(() -> run());
            }
        }

    }
}
